from pydantic import TypeAdapter
from celery import shared_task
from functools import partial
from typing import List
from django.db.models import Count, Sum
from core.step_model import SuiteCondition
from apps.suites.models import Suite
from apps.tests.models import Case, Tag, CaseRunLog
from apps.envs.models import Env
from apps.reports.models import Report
from core.run_one_case import run_one_case
from core.common import loop_for_iterator, get_env_params_by_env_id

success_case_filter = {
    'result': CaseRunLog.CaseResult.SUCCESS
}

fail_case_filter = {
    'result': CaseRunLog.CaseResult.FAIL
}

error_case_filter = {
    'result': CaseRunLog.CaseResult.ERROR
}

api_case_filter = {
    'case__type': Case.CaseType.API
}
ui_case_filter = {
    'case__type': Case.CaseType.UI
}


api_success_case_filter = api_case_filter | success_case_filter
api_fail_case_filter = api_case_filter | fail_case_filter
api_error_case_filter = api_case_filter | error_case_filter
ui_success_case_filter = ui_case_filter | success_case_filter
ui_fail_case_filter = ui_case_filter | fail_case_filter
ui_error_case_filter = ui_case_filter | error_case_filter


class SuiteFilterMethod:
    ByCaseType = 1  # 按用例类型执行
    ByModule = 2  # 按用例所属模块执行
    ByTag = 3   # 按用例所属标签执行


def get_cases_by_type(project_id, value):
    """
    通过用例类型获取用例ID集合
    """
    return set(Case.objects.all().filter(project=project_id,
                                         type=value, is_delete=False).values_list('id', flat=True))


def get_cases_by_module(project_id, value):
    """
    通过用例所属模块获取用例ID集合
    """
    return set(Case.objects.all().filter(project=project_id,
                                         module__in=value, is_delete=False).values_list('id', flat=True))


def get_cases_by_tag(project_id, value):
    """
    通过用例标签获取用例ID集合
    """
    return set(Tag.objects.all().get(project=project_id,
                                     id=value).case_set.all().filter(is_delete=False).values_list('id', flat=True))


GetCasesFilterMap = {
    SuiteFilterMethod.ByCaseType: get_cases_by_type,
    SuiteFilterMethod.ByModule: get_cases_by_module,
    SuiteFilterMethod.ByTag: get_cases_by_tag
}


def get_suite_cases(suite_obj):
    """
    获取套件下所有的用例ID
    """
    project_id = suite_obj.project
    if suite_obj.conditions:
        cases = set()
        suite_conditions_obj: List[SuiteCondition] = TypeAdapter(List[SuiteCondition]).validate_python(
            suite_obj.conditions)
        for index, suite_condition_obj in enumerate(suite_conditions_obj):
            method, value = suite_condition_obj.method, suite_condition_obj.value
            sub_cases = GetCasesFilterMap.get(method)(project_id, value)
            if index == 0:
                cases = cases.union(sub_cases)
            else:
                cases = cases.intersection(sub_cases) if suite_conditions_obj[index - 1].andOr == 'And' else cases.union(sub_cases)
        return list(cases)

    else:
        cases = Case.objects.all().filter(project=project_id, is_delete=False).values('id')
        return cases


def get_case_number(report_obj, **kwargs):
    return len(CaseRunLog.objects.filter(report=report_obj, **kwargs))


def get_module_case_number(report_obj, module_id, **kwargs):
    return len(CaseRunLog.objects.filter(report=report_obj, case__module=module_id, **kwargs))


def get_tag_case_number(report_obj, tag_id, **kwargs):
    return len(CaseRunLog.objects.filter(report=report_obj, case__tag=tag_id, **kwargs))


def count_report_info(case_obj, report_obj: Report):
    case_plant_name = case_obj.module.plant.name
    case_module_name = case_obj.module.name
    module_id = case_obj.module.id
    module_filter_obj = {'text': case_module_name, 'value': case_plant_name}
    plant_filter_obj = {'text': case_plant_name, 'value': case_plant_name}
    if module_filter_obj not in report_obj.detail['module_filter']:
        report_obj.detail['module_filter'].append(module_filter_obj)

    if plant_filter_obj not in report_obj.detail['plant_filter']:
        report_obj.detail['plant_filter'].append(plant_filter_obj)

    report_obj.detail['module'].setdefault(module_id, {})
    report_obj.detail['module'][module_id]['plant_name'] = case_plant_name
    report_obj.detail['module'][module_id]['module_name'] = case_module_name
    report_obj.detail['module'][module_id]['all_case_number'] = get_module_case_number(report_obj, module_id)
    report_obj.detail['module'][module_id]['success_number'] = get_module_case_number(report_obj, module_id,
                                                                                      **success_case_filter)
    report_obj.detail['module'][module_id]['fail_number'] = get_module_case_number(report_obj, module_id,
                                                                                   **fail_case_filter)
    report_obj.detail['module'][module_id]['error_number'] = get_module_case_number(report_obj, module_id,
                                                                                    **error_case_filter)

    case_tags = case_obj.tag.all()
    for tag_obj in case_tags:
        tag_filter_obj = {'text': tag_obj.name, 'value': tag_obj.name}
        if tag_filter_obj not in report_obj.detail['tag_filter']:
            report_obj.detail['tag_filter'].append(tag_filter_obj)

        report_obj.detail['tag'].setdefault(tag_obj.id, {})

        report_obj.detail['tag'][tag_obj.id]['name'] = tag_obj.name
        report_obj.detail['tag'][tag_obj.id]['all_case_number'] = get_tag_case_number(report_obj, tag_obj.id)
        report_obj.detail['tag'][tag_obj.id]['success_number'] = get_tag_case_number(report_obj, tag_obj.id,
                                                                                     **success_case_filter)
        report_obj.detail['tag'][tag_obj.id]['fail_number'] = get_tag_case_number(report_obj, tag_obj.id,
                                                                                  **fail_case_filter)
        report_obj.detail['tag'][tag_obj.id]['error_number'] = get_tag_case_number(report_obj, tag_obj.id,
                                                                                   **error_case_filter)

    # report_obj.all_case_number = get_case_number(report_obj)
    report_obj.success_case_number = get_case_number(report_obj, **success_case_filter)
    report_obj.fail_case_number = get_case_number(report_obj, **fail_case_filter)
    report_obj.error_case_number = get_case_number(report_obj, **error_case_filter)
    report_obj.api_case_number = get_case_number(report_obj, **api_case_filter)
    report_obj.api_success_case_number = get_case_number(report_obj, **api_success_case_filter)
    report_obj.api_fail_case_number = get_case_number(report_obj, **api_fail_case_filter)
    report_obj.api_error_case_number = get_case_number(report_obj, **api_error_case_filter)
    report_obj.ui_case_number = get_case_number(report_obj, **ui_case_filter)
    report_obj.ui_success_case_number = get_case_number(report_obj, **ui_success_case_filter)
    report_obj.ui_fail_case_number = get_case_number(report_obj, **ui_fail_case_filter)
    report_obj.ui_error_case_number = get_case_number(report_obj, **ui_error_case_filter)


def run_many_case(env_id, user_id, report_obj, case_id):

    case_obj, case_logs_obj = run_one_case(env_id=env_id, case_id=case_id, user_id=user_id, report_id=report_obj)
    count_report_info(case_obj, report_obj)
    report_obj.save()


@shared_task
def run_suite(env_id, suite_id, user_id):
    env_name = Env.objects.all().get(id=env_id).name
    suite_obj = Suite.objects.all().get(id=suite_id)
    cases = get_suite_cases(suite_obj)
    report_obj = Report.objects.create(name=suite_obj.name, all_case_number=len(cases), project=suite_obj.project,
                                       create_by_id=user_id, update_by_id=user_id, run_env_name=env_name,
                                       detail={'plant_filter': [], 'tag_filter': [], 'module_filter': [],
                                               'module': {}, 'tag': {}})
    loop_for_iterator(map(partial(run_many_case, env_id, user_id, report_obj), cases))







